﻿using System;
using System.Collections.Generic;

using static Apewer.TextUtility;

#if !NET20
using System.Dynamic;
#endif

namespace Apewer
{

    /// <summary>对象字典模型。</summary>
    [Serializable]
    public class ObjectSet : ObjectSet<object>
    {

        /// <summary>构造函数。</summary>
        /// <param name="dictionary">引用指定的字典，指定 NULL 时创建新字典。</param>
        /// <param name="always">对动态属性的访问始终返回 TRUE 值。</param>
        public ObjectSet(Dictionary<string, object> dictionary, bool always = false) : base(dictionary, always) { }

        /// <summary></summary>
        /// <param name="trimKey">使用 get 和 set 访问器时候修建 Key。</param>
        /// <param name="always">对动态属性的访问始终返回 TRUE 值。</param>
        public ObjectSet(bool trimKey = false, bool always = false) : base(trimKey, always) { }

    }

    /// <summary>字典模型。</summary>
    [Serializable]
    public partial class ObjectSet<T> : IToJson
    {

        private Dictionary<string, T> _origin;
        private bool _trimkey = false;
        private bool _locked = false;
        internal bool _always = false;

        /// <summary>获取或设置字典内容。</summary>
        public T this[string key] { get { return Get(key); } set { Set(key, value); } }

        /// <summary>获取字典。</summary>
        internal Dictionary<string, T> Origin { get { return _origin; } }

        internal bool Locked { get { return _locked; } set { _locked = value; } }

        internal bool TrimKey { get { return _trimkey; } set { _trimkey = value; } }

        /// <summary>构造函数。</summary>
        /// <param name="dictionary">引用指定的字典，指定 NULL 时创建新字典。</param>
        /// <param name="always">对动态属性的访问始终返回 TRUE 值。</param>
        public ObjectSet(Dictionary<string, T> dictionary, bool always = false)
        {
            _trimkey = false;
            _always = always;
            _origin = dictionary ?? new Dictionary<string, T>();
        }

        /// <summary>构造函数。</summary>
        /// <param name="trimKey">使用 get 和 set 访问器时候修建 Key。</param>
        /// <param name="always">对动态属性的访问始终返回 TRUE 值。</param>
        public ObjectSet(bool trimKey = false, bool always = false)
        {
            _trimkey = trimKey;
            _always = always;
            _origin = new Dictionary<string, T>();
        }

        private T Get(string key)
        {
            var contains = false;
            return Get(key, ref contains);
        }

        private T Get(string key, ref bool contains)
        {
            var value = default(T);
            if (key != null)
            {
                var k = _trimkey ? Trim(key) : key;
                lock (_origin)
                {
                    contains = _origin.ContainsKey(k);
                    if (contains) value = _origin[k];
                }
            }
            return value;
        }

        private bool Set(string key, T value)
        {
            if (_locked) return false;
            var success = false;
            if (key != null)
            {
                var k = _trimkey ? Trim(key) : key;
                lock (_origin)
                {
                    if (_origin.ContainsKey(k)) _origin[k] = value;
                    else _origin.Add(k, value);
                    success = true;
                }
            }
            return success;
        }

        /// <summary>转换为 <see cref="Json"/> 对象。</summary>
        public Json ToJson() => Json.From(_origin);

        /// <summary>转换为 <see cref="Json"/> 字符串。</summary>
        public override string ToString()
        {
            var json = ToJson();
            if (json == null) return null;
            return json.ToString(true);
        }

    }

#if !NET20

    /// <summary>字典模型。</summary>
    public partial class ObjectSet<T> : DynamicObject
    {

        /// <summary></summary>
        public override IEnumerable<string> GetDynamicMemberNames()
        {
            return new List<string>(_origin.Keys).ToArray();
        }

        /// <summary></summary>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            var contains = false;
            result = Get(binder.Name, ref contains);
            if (_always) return true;
            return contains;
        }

        /// <summary></summary>
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            var setted = Set(binder.Name, (T)value);
            if (_always) return true;
            return setted;
        }

        internal static ExpandoObject Expando(ObjectSet<T> os)
        {
            if (os == null) return null;
            var eo = new ExpandoObject();
            var dict = eo as IDictionary<string, object>;
            foreach (var kvp in os._origin) dict.Add(kvp.Key, kvp.Value);
            return eo;
        }

        internal static ExpandoObject[] Expando(ObjectSet<T>[] oss)
        {
            if (oss == null) return null;
            var eos = new ExpandoObject[oss.Length];
            for (var i = 0; i < oss.Length; i++) eos[i] = Expando(oss[i]);
            return eos;
        }

    }

#endif

}
